﻿using System.Collections.Generic;
using System.Configuration;
using System.IO;
using FunctionalCLR;
using FunctionalCLR.Core;
using FunctionalCLR.Core.Function;
using System;

namespace FunctionalCLR.Shell {
    public class Interpreter {

        private Dictionary<string, ICommand> commands
            = new Dictionary<string, ICommand>();

        public IContext Context {
            get;
            set;
        }

        protected int LineNumber = 0;

        public Interpreter(IContext context) {
            this.Context = context;
        }

        private string fileName = string.Empty;

        public void Run(string fileName) {
            this.Run(fileName, new string[] { });
        }

        public void Run(string fileName, string[] args) {
            FileStream fs = new FileStream(fileName, FileMode.Open);
            StreamReader sr = new StreamReader(fs);

            this.Execute(sr.ReadToEnd(), args);
            sr.Close();
        }

        public void Execute(string source) {
            this.Execute(source, new string[] { });
        }

        public void Execute(string source, string[] args) {
            string[] stmts = source.Split(';');
            LineNumber = 0;
            foreach (string stmt in stmts) {
                LineNumber++;
                this.Process(stmt);
            }

            FuncNode mainEntry 
                = this.Context.GetFunctionManager().Get("Main");
            if (mainEntry != null) {
                string expr = Utils.InitUDF(
                    mainEntry as UserDefinedFunctionNode, args);

                object result 
                    = DefaultDependencyManager.Instance.ParserFactory.Evaluate(
                    expr);
                Console.WriteLine(result.ToString());
            }
        }

        public object Process(string input) {
            if (string.IsNullOrEmpty(input)) {
                return string.Empty;
            }

            //((Context)this.Context).InputHistory.Add(input);
            object output = null;

            try {
                Utils.Debugging("input: " + input);
                output = this.ProcessInternal(input);
                //((Context)this.Context).OutputHistory.Add(output);
            } catch (Exception ex) {
                //((Context)this.Context).ErrorHistory.Add(ex);
                //Utils.Log(ex.Message + "\n" + ex.StackTrace);
                throw ex;
            }

            return output;
        }

        protected virtual object ProcessInternal(string input) {
            string output = string.Empty;

            Lexer lexer = new Lexer(input);
            Token token = lexer.PeekToken(1);
            Token nt = lexer.PeekToken(2);
            if (token.TokenType == TokenType.Identifier
                && token.Value == "class") {
                UserDefinedType type = new UserDefinedType();

                type.Name = nt.Value;
                lexer.NextToken();
                lexer.NextToken();

                lexer.NextToken("{");
                while (
                    (token = lexer.NextToken()).TokenType
                    != TokenType.RightBracket) {
                    break;
                }
            }
            if (input.Contains(":=")) {
                // it is a function def
                return AddFunction(input);
            }

            return this.Context.Evaluate(input);
        }

        public void LoadSession(string fileName) {
            FileStream fs = new FileStream(fileName, FileMode.Open);
            StreamReader sr = new StreamReader(fs);

            string input = string.Empty;
            while ((input = sr.ReadLine()) != null) {
                object output = Process(input);
            }

            sr.Close();
        }

        public string AddFunction(string input) {
            UserDefinedFunctionParser udfp = new UserDefinedFunctionParser(Context.GetParserFactory());
            UserDefinedFunctionNode udf = udfp.Parse(input);

            this.Context.GetFunctionManager().Add(udf.Name, udf);
            return string.Format("Function {0} was created.", udf.Name);
        }

    }
}
